home *** CD-ROM | disk | FTP | other *** search
- /*
- linux/fs/dmsdos/dmsdos_alloc.c
-
- DMSDOS filesystem: cluster allocation functions
-
- ******************************************************************************
- DMSDOS (Doublespace/Drivespace compressed MSDOS filesystem) for Linux
- written 1995,1996 by Frank Gockel
-
- (C) Copyright 1995,1996 by Frank Gockel
-
- Some code of the dmsdos filesystem has been copied from the msdos filesystem
- so there are the following additional copyrights:
-
- (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
- (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
- (C) Copyright 1992-1995 by Linus Torvalds
-
- The DMSDOS filesystem was inspired by the THS filesystem (a simple doublespace
- DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
-
- The DMSDOS filesystem is distributed under the Gnu General Public Licence.
- See file COPYING for details.
- ******************************************************************************
-
- */
-
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/msdos_fs.h>
- #include <linux/dmsdos_fs.h>
- #include <linux/string.h>
- #include <linux/sched.h>
-
- extern Dblsb dblsb[];
-
- #define NEAR_AREA 512
- /* no change for doublespace, but fixes drivespace 3 problems (was 50) */
- #define BIG_HOLE (dblsb[cvfnr].s_sectperclust*3+2)
-
- struct wait_queue * alloc_wait = NULL;
- int alloc_lock=0;
- void lock_alloc(void)
- { while(alloc_lock)sleep_on(&alloc_wait);
- alloc_lock=1;
- }
- void unlock_alloc(void)
- { alloc_lock=0;
- wake_up(&alloc_wait);
- }
-
- struct wait_queue * readwrite_wait = NULL;
- int readwrite_lock=0;
- void lock_readwrite(void)
- { while(readwrite_lock)sleep_on(&readwrite_wait);
- readwrite_lock=1;
- }
- void unlock_readwrite(void)
- { readwrite_lock=0;
- wake_up(&readwrite_wait);
- }
-
- void free_chain(struct super_block*sb, int clusternr, int cvfnr)
- {
- Mdfat_entry mde,dummy,newmde;
- int newval=0;
- int i;
- int sektors;
- int sektornr;
- int merk;
-
- lock_alloc();
- while(clusternr>0)
- { /*printk("DMSDOS: free chain: freeing cluster %d\n",clusternr);*/
- /* read mdfat entry and clear */
- newmde.sector_minus_1=0;
- newmde.size_lo_minus_1=0;
- newmde.size_hi_minus_1=0;
- newmde.flags=0;
- dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
- dbl_mdfat_value(sb,clusternr,cvfnr,&newmde,&dummy);
- sektors=mde.size_lo_minus_1+1;
- sektornr=mde.sector_minus_1+1;
- if(mde.flags&2)
- {
- /* free sectors in bitfat */
- for(i=0;i<sektors;++i)dbl_bitfat_value(sb,sektornr+i,cvfnr,&newval);
- dblsb[cvfnr].s_full=0;
- }
- else
- { printk("DMSDOS: CVF %d: invalid MDFAT entry for cluster %d, zeroing it - check filesystem\n",
- cvfnr+1,clusternr);
- }
- /* read fat entry and clear */
- merk=clusternr;
- clusternr=dbl_fat_nextcluster(sb,clusternr,cvfnr,NULL);
- dbl_fat_nextcluster(sb,merk,cvfnr,&newval);
- }
- unlock_alloc();
- }
-
- int find_free_bitfat(struct super_block*sb, int sektornr,int cvfnr, int size)
- { int testsek;
- int i;
-
- if(sektornr>=dblsb[cvfnr].s_datastart&&sektornr<=dblsb[cvfnr].s_dataend-size)
- { /* search exactly fitting hole near sektornr */
- testsek=sektornr;
- while(testsek<sektornr+NEAR_AREA)
- { if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
- { ++testsek;
- continue;
- }
- i=1;
- while(i<=size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
- if(i==size)
- { dblsb[cvfnr].s_full=0;
- return testsek;
- }
- testsek+=i;
- }
- testsek=sektornr;
- while(testsek>sektornr-NEAR_AREA)
- { if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
- { --testsek;
- continue;
- }
- i=1;
- while(i<=size&&dbl_bitfat_value(sb,testsek-i,cvfnr,NULL)==0)++i;
- if(i==size)
- { dblsb[cvfnr].s_full=0;
- return testsek-i+1;
- }
- testsek-=i;
- }
- }
-
- /* search for a big hole */
- testsek=dblsb[cvfnr].s_datastart;
- while(testsek<=dblsb[cvfnr].s_dataend-size)
- { if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
- { ++testsek;
- continue;
- }
- i=1;
- while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
- if(i==BIG_HOLE)
- { dblsb[cvfnr].s_full=0;
- return testsek;
- }
- testsek+=i;
- }
-
- /* search for an exactly fitting hole */
- testsek=dblsb[cvfnr].s_datastart;
- while(testsek<=dblsb[cvfnr].s_dataend-size)
- { if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
- { ++testsek;
- continue;
- }
- i=1;
- while(i<=size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
- if(i==size)
- { dblsb[cvfnr].s_full=0;
- return testsek;
- }
- testsek+=i;
- }
-
- if(dblsb[cvfnr].s_full==0)
- printk(KERN_EMERG "DMSDOS: CVF %d almost full or highly fragmented at MDFAT level, refusing further non-root write access.\n",
- cvfnr+1);
-
- dblsb[cvfnr].s_full=1;
- /* last trial: search for any hole >= size */
- testsek=dblsb[cvfnr].s_datastart;
- while(testsek<=dblsb[cvfnr].s_dataend-size)
- { if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
- { ++testsek;
- continue;
- }
- i=1;
- while(i<size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
- if(i==size)return testsek;
- testsek+=i;
- }
-
- /* not found, means disk full or MDFAT too fragmented */
- dblsb[cvfnr].s_full=2;
- printk(KERN_EMERG "DMSDOS: CVF %d full or too fragmented at MDFAT level - see doc\n",cvfnr+1);
- return 0;
- }
-
- /* finds a free cluster, marks it as end in fat,
- returns clusternr
- returns -ENOSPC if disk full
-
- **** do not call directly, call the user preferable allocation functions
- instead
- */
- int allocate_cluster(struct super_block*sb, int cvfnr)
- {
- int i;
- int newval;
- int clusternr;
- Mdfat_entry mde;
-
- /* when filesystem is almost full, only root may use the last BIG_HOLE
- sectors since it can be dangerous to fill a CVF upto its neck
- - so ignore the s_full flag on root */
- if(dblsb[cvfnr].s_full!=0&¤t->euid!=0)return -ENOSPC;
-
- /* if there's *really* nothing free any more, don't let root write */
- if(dblsb[cvfnr].s_full==2)return -ENOSPC;
-
- /* find free cluster in fat */
- clusternr=0;
- for(i=2;i<=dblsb[cvfnr].s_max_cluster;++i)
- { if(dbl_fat_nextcluster(sb,i,cvfnr,NULL)==0)
- { clusternr=i;
-
- /* check mdfat if empty */
- dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
- if(mde.flags&2)
- { printk("DMSDOS: CVF %d: MDFAT contradicts FAT for cluster %d, ignoring entry - check filesystem!\n",
- cvfnr+1,clusternr);
- }
- else
- {
- /* okay, mark appended cluster as 'end' */
- newval=0xFFFF;
- dbl_fat_nextcluster(sb,clusternr,cvfnr,&newval);
-
- return clusternr;
- }
- }
- }
-
- return -ENOSPC;
- }
-
- /* user preferable cluster allocation routines */
-
- int allocate_first_cluster(struct super_block*sb, int cvfnr)
- { int res;
-
- lock_alloc();
- res=allocate_cluster(sb,cvfnr);
- unlock_alloc();
- return res;
- }
-
- int allocate_next_cluster(struct super_block*sb, int akt_cluster, int cvfnr)
- {
- int res;
-
- lock_alloc();
- res=allocate_cluster(sb,cvfnr);
- if(res>0)dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&res);
- unlock_alloc();
- return res;
- }
-
- /* replaces an existing cluster;
- this unusual function must be called before rewriting any file cluster;
- *** size must be known (encoded in mde) ***
- it does nothing if called too often;
- returns first sector nr
- */
-
- int replace_existing_cluster(struct super_block*sb, int cluster,
- int near_sector,
- Mdfat_entry*mde, int cvfnr)
- { Mdfat_entry old_mde,new_mde,dummy;
- int i;
- int newval;
- int sektor;
- int old_sektor;
- int old_size;
- int new_size;
-
- lock_alloc();
-
- /* printk("DMSDOS: replace_existing_cluster cluster=%d near_sector=%d mdfatbits=0x%x cvfnr=%d\n",
- cluster,near_sector,mdfatbits,cvfnr+1);*/
- dbl_mdfat_value(sb,cluster,cvfnr,NULL,&old_mde);
- old_size=old_mde.size_lo_minus_1+1;
- old_sektor=old_mde.sector_minus_1+1;
- new_size=mde->size_lo_minus_1+1;
- if(old_mde.flags&2)
- {
- /* test whether same length */
- if(old_size==new_size)
- { /*printk("DMSDOS: replace_existing_cluster: same length, ok\n");*/
- sektor=old_sektor;
- goto mdfat_update;
- }
- /* different length, replace mdfat entry */
- newval=0;
- /*printk("DMSDOS: replace_existing_cluster: freeing old sectors...\n");*/
- for(i=0;i<old_size;++i)dbl_bitfat_value(sb,old_sektor+i,cvfnr,&newval);
- /*printk("DMSDOS: replace_existing_cluster: freeing finished\n");*/
- }
- /*printk("DMSDOS: replace_existing_cluster: call find_free_bitfat...\n");*/
- sektor=find_free_bitfat(sb,near_sector,cvfnr,new_size);
- /*printk("DMSDOS: replace_existing_cluster: find_free_bitfat returned %d\n",
- sektor);*/
- if(sektor==0)
- { if(old_mde.flags&2)
- {
- /* undo bitfat free */
- newval=1;
- for(i=0;i<old_size;++i)dbl_bitfat_value(sb,old_sektor+i,cvfnr,&newval);
- }
- unlock_alloc();
- return -ENOSPC; /* disk full */
- }
- /* check whether really free (bug supposed in find_free_bitfat) */
- for(i=0;i<new_size;++i)
- { if(dbl_bitfat_value(sb,sektor+i,cvfnr,NULL))
- { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sektor %d size %d but they are not all free!\n",
- sektor,new_size);
- unlock_alloc();
- panic("This is a dmsdos bug - reboot and check filesystem\n");
- return -EIO;
- }
- }
- newval=1;
- /*printk("DMSDOS: replace_existing_cluster: allocating in bitfat...\n");*/
- for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sektor+i,cvfnr,&newval);
-
- mdfat_update:
- new_mde.sector_minus_1=sektor-1;
- new_mde.size_lo_minus_1=mde->size_lo_minus_1;
- new_mde.size_hi_minus_1=mde->size_hi_minus_1;
- new_mde.flags=mde->flags|2;
- /*printk("DMSDOS: replace_existing_cluster: writing mdfat...\n");*/
- dbl_mdfat_value(sb,cluster,cvfnr,&new_mde,&dummy);
- unlock_alloc();
- return sektor; /* okay */
- }
-
- /* user preferable cluster allocation functions for directories */
- /* cluster is uncompressed, of maximum size and zerod out and written */
-
- int allocate_first_dir_cluster(struct super_block*sb,int cvfnr)
- { int res;
- int sektor;
- int cluster;
- int i;
- int size;
- struct buffer_head*bh;
- int newval;
- Mdfat_entry mde;
-
- res=allocate_first_cluster(sb,cvfnr);
- if(res<=0)return res;
- cluster=res;
- size=dblsb[cvfnr].s_sectperclust;
- mde.size_lo_minus_1=size-1;
- mde.size_hi_minus_1=size-1;
- mde.flags=3;
- res=replace_existing_cluster(sb,cluster,0,&mde,cvfnr);
- if(res<0)
- { /* undo cluster allocation */
- newval=0;
- dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
- return res;
- }
-
- /* zero it out since it is an empty dir cluster */
- sektor=res;
- for(i=0;i<size;++i)
- { bh=noread_dbl_sector(sb,sektor+i,cvfnr);
- if(bh==NULL)
- { /* undo cluster allocation */
- newval=0;
- dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
- return -EIO;
- }
- memset(bh->b_data,0,SECTOR_SIZE);
- bh_dirty(sb,bh);
- bh_free(sb,bh);
- }
-
- return cluster;
- }
-
- int allocate_next_dir_cluster(struct super_block*sb, int akt_cluster,
- int cvfnr)
- { int res;
- int sektor;
- int i;
- struct buffer_head*bh;
- int cluster;
- int size;
- int newval;
- Mdfat_entry mde;
-
- res=allocate_next_cluster(sb,akt_cluster,cvfnr);
- if(res<=0)return res;
- cluster=res;
- size=dblsb[cvfnr].s_sectperclust;
- mde.size_lo_minus_1=size-1;
- mde.size_hi_minus_1=size-1;
- mde.flags=3;
- sektor=dbl_mdfat_cluster2sector(sb,akt_cluster,cvfnr);
- res=replace_existing_cluster(sb,cluster,sektor,&mde,cvfnr);
- if(res<0)
- { /* undo cluster allocation */
- newval=0;
- dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
- newval=-1;
- dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&newval);
- return res;
- }
-
- /* zero it out since it is an empty dir cluster */
- sektor=res;
- for(i=0;i<size;++i)
- { bh=noread_dbl_sector(sb,sektor+i,cvfnr);
- if(bh==NULL)
- { /* undo cluster allocation */
- newval=0;
- dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
- newval=-1; /* cut off or directory will be unreadable */
- dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&newval);
- return -EIO;
- }
- memset(bh->b_data,0,SECTOR_SIZE);
- bh_dirty(sb,bh);
- bh_free(sb,bh);
- }
-
- return cluster;
- }
-
- int scan_dbl_dir_4_empty(struct super_block*sb, int dirstartclust, int cvfnr)
- { return scan_dbl_dir_4_n_empty(sb,dirstartclust,cvfnr,1,NULL);
- }
-
- /* find n empty dir entries
- append a dir cluster if necessary
- fills lfn_inos if !=NULL
- */
-
- int scan_dbl_dir_4_n_empty(struct super_block*sb, int dirstartclust, int cvfnr,
- int n,int*lfn_inos)
- { int ino;
- int ent=0;
- unsigned char buf[32];
- int lastcluster;
- int merk;
- struct inode * inode;
- int found=0;
-
- /* fill first unused with zero -- for safety */
- if(lfn_inos)lfn_inos[n]=0;
-
- while((ino=read_dbl_direntry(sb,dirstartclust,cvfnr,ent,buf))>0)
- { ++ent;
- if(buf[0]==0xe5||buf[0]==0)
- { inode=iget(sb,ino);
- if(MSDOS_I(inode)->i_busy==0)
- { iput(inode);
- ++found;
- if(lfn_inos)lfn_inos[n-found]=ino;
- if(n==found)return ino;
- }
- else
- { found=0;
- iput(inode);
- }
- }
- else found=0;
-
- }
- if(dirstartclust==0)return -ENOSPC; /* root dir full */
-
- if(ent%(dblsb[cvfnr].s_sectperclust*16))
- { printk("DMSDOS: scan_dbl_dir_4_empty: scan error occured, refusing to append cluster\n");
- return -ENOSPC;
- }
-
- /* okay, must append a cluster */
- lastcluster=dirstartclust;
- while((merk=dbl_fat_nextcluster(sb,lastcluster,cvfnr,NULL))>0)
- lastcluster=merk;
- lastcluster=allocate_next_dir_cluster(sb,lastcluster,cvfnr);
- if(lastcluster<0) return -ENOSPC; /* disk full */
- /* the cluster is already zerod out */
-
- while(found<n)
- {
- ino=read_dbl_direntry(sb,dirstartclust,cvfnr,ent,buf);
- if(ino<0)
- { printk("DMSDOS: scan_dbl_dir_4_empty: error after appending dir cluster\n");
- return ino;
- }
- ++found;++ent;
- if(lfn_inos)lfn_inos[n-found]=ino;
- }
- return ino;
- }
-
- int moveback(struct super_block*sb, int clusternr, int cvfnr)
- { Mdfat_entry mde,dummy;
- int sector;
- int i,newval;
- struct buffer_head*bh1,*bh2;
-
- if(clusternr<2||clusternr>dblsb[cvfnr].s_max_cluster)return -EINVAL;
-
- lock_alloc();
-
- dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
- if(mde.flags&2) /* used */
- { sector=mde.sector_minus_1;
- if(dbl_bitfat_value(sb,sector,cvfnr,NULL)==0)
- { while(dbl_bitfat_value(sb,sector-1,cvfnr,NULL)==0)--sector;
- /* copy cluster */
- /* printk("DMSDOS: moving cluster %d from sector %ld to %d\n",clusternr,
- mde.sector_minus_1+1,sector);*/
- for(i=0;i<=mde.size_lo_minus_1;++i)
- { bh1=read_dbl_sector(sb,mde.sector_minus_1+1+i,cvfnr);
- if(bh1==NULL) goto err_out;
- bh2=noread_dbl_sector(sb,sector+i,cvfnr);
- if(bh2==NULL)
- { bh_free(sb,bh1);
- err_out:
- unlock_alloc();
- printk("DMSDOS: moveback failed (couldn't read data)!\n");
- return -EIO;
- }
- memcpy(bh2->b_data,bh1->b_data,SECTOR_SIZE);
- bh_dirty(sb,bh2);
- bh_free(sb,bh1);
- bh_free(sb,bh2);
- }
- /* free in bitfat */
- newval=0;
- for(i=0;i<=mde.size_lo_minus_1;++i)
- dbl_bitfat_value(sb,mde.sector_minus_1+1+i,cvfnr,&newval);
- /* allocate in bitfat */
- newval=1;
- for(i=0;i<=mde.size_lo_minus_1;++i)
- dbl_bitfat_value(sb,sector+i,cvfnr,&newval);
- /* save in mdfat */
- mde.sector_minus_1=sector-1;
- dbl_mdfat_value(sb,clusternr,cvfnr,&mde,&dummy);
- }
- }
-
- unlock_alloc();
- return 0;
- }